package com.yanyu.space.sys.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yanyu.space.common.core.exception.ServiceException;
import com.yanyu.space.sys.bean.enums.EnabledEnum;
import com.yanyu.space.sys.bean.enums.SysRCode;
import com.yanyu.space.sys.bean.po.Dept;
import com.yanyu.space.sys.bean.po.User;
import com.yanyu.space.sys.bean.vo.common.TreeSelect;
import com.yanyu.space.sys.bean.vo.dept.DeptAddVo;
import com.yanyu.space.sys.bean.vo.dept.DeptUpdateVo;
import com.yanyu.space.sys.bean.vo.dept.RoleDeptTreeselectVo;
import com.yanyu.space.sys.dao.DeptMapper;
import com.yanyu.space.sys.dao.UserMapper;
import com.yanyu.space.sys.service.IDeptService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.cloud.sleuth.annotation.SpanTag;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author yanyu
 */
@Service
@Slf4j
@AllArgsConstructor
public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements IDeptService {

    private UserMapper userMapper;

    @Override
    public List<Dept> list(@SpanTag(key = "deptName") String deptName, @SpanTag(key = "enabled") Integer enabled) {
        List<Dept> list = baseMapper.selectList(Wrappers.<Dept>lambdaQuery()
                .like(StringUtils.isNoneBlank(deptName), Dept::getDeptName, deptName)
                .eq(enabled != null, Dept::getEnabled, enabled)
                .orderByAsc(Dept::getSort).orderByDesc(Dept::getUpdateTime));
        return list;
    }

    @Override
    public Dept getById(Long id) {
        return baseMapper.selectById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(DeptAddVo vo) throws InterruptedException {
        Dept dept = BeanUtil.copyProperties(vo, Dept.class);
        checkDeptNameUnique(dept);
        checkOwnParentDisabled(dept);
        baseMapper.insert(dept);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateById(DeptUpdateVo vo) {
        Dept dept = BeanUtil.copyProperties(vo, Dept.class);
        checkDeptNameUnique(dept);
        checkOwnChildEnabled(dept);
        checkOwnParentDisabled(dept);
        baseMapper.updateById(dept);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        long childCnt = baseMapper.selectCount(Wrappers.<Dept>lambdaQuery().eq(Dept::getParentId, id));
        if (childCnt > 0) {
            throw new ServiceException(SysRCode.DEPT_HAS_CHILD);
        }
        long deptWithUser = userMapper.selectCount(Wrappers.<User>lambdaQuery().eq(User::getDeptId, id));
        if (deptWithUser > 0) {
            throw new ServiceException(SysRCode.DEPT_HAS_USER);
        }
        baseMapper.deleteById(id);
    }

    @Override
    public List<TreeSelect> treeselect() {
        List<Dept> depts = baseMapper.selectList(Wrappers.<Dept>lambdaQuery().orderByAsc(Dept::getSort).orderByDesc(Dept::getUpdateTime));
        return depts.parallelStream()
                .filter(deptTmp -> deptTmp.getParentId().equals(0L))
                .map(deptTmp -> {
                    TreeSelect treeSelect = new TreeSelect();
                    treeSelect.setId(deptTmp.getId());
                    treeSelect.setLabel(deptTmp.getDeptName());
                    treeSelect.setChildren(buildTreeselect(deptTmp, depts));
                    return treeSelect;
                }).collect(Collectors.toList());
    }

    @Override
    public RoleDeptTreeselectVo roleDeptTreeselect(Long roleId) {
        RoleDeptTreeselectVo vo = new RoleDeptTreeselectVo();
        List<Dept> depts = baseMapper.listDeptByRoleId(roleId);
        vo.setDepts(treeselect());
        vo.setCheckedKeys(depts.parallelStream().map(Dept::getId).collect(Collectors.toList()));
        return vo;
    }

    @Override
    public List<Dept> listChildren(Long deptId) {
        if (deptId == null) {
            return (List<Dept>) CollectionUtils.EMPTY_COLLECTION;
        }
        List<Dept> allDepts = new ArrayList<>();
        Dept dept = baseMapper.selectById(deptId);
        allDepts.add(dept);
        buildOwnChild(Arrays.asList(dept), allDepts);
        return allDepts;
    }

    @Override
    public List<Dept> listExclude(Long id) {
        List<Dept> list = baseMapper.selectList(Wrappers.<Dept>lambdaQuery()
                .notIn(Dept::getId, id));
        Dept excludeDept = baseMapper.selectById(id);
        if (!excludeDept.getParentId().equals(0L)) {
            List<Long> allExcludeDeptIds = new ArrayList<>();
            allExcludeDeptIds.add(id);
            buildOwnChild(excludeDept, list, allExcludeDeptIds);
            List<Dept> allExcludeDepts = baseMapper.selectList(Wrappers.<Dept>lambdaQuery()
                    .notIn(Dept::getId, allExcludeDeptIds));
            return allExcludeDepts;
        }
        return (List<Dept>) CollectionUtils.EMPTY_COLLECTION;
    }

    /**
     * 构建部门树，该部门的孩子节点
     *
     * @param dept
     * @param allDepts
     * @param ownChildIds
     */
    private void buildOwnChild(Dept dept, List<Dept> allDepts, List<Long> ownChildIds) {
        allDepts
                .stream()
                .filter(deptTem -> dept.getId().equals(deptTem.getParentId()))
                .forEach(deptTem -> {
                    buildOwnChild(deptTem, allDepts, ownChildIds);
                    ownChildIds.add(deptTem.getId());
                });
    }

    /**
     * 构建部门树
     *
     * @param depts
     * @param allDepts
     */
    private void buildOwnChild(List<Dept> depts, List<Dept> allDepts) {
        for (Dept dept : depts) {
            List<Dept> deptTmps = baseMapper.selectList(Wrappers.<Dept>lambdaQuery().eq(Dept::getParentId, dept.getId()));
            if (CollectionUtils.isNotEmpty(deptTmps)) {
                allDepts.addAll(deptTmps);
                buildOwnChild(deptTmps, allDepts);
            }
        }
    }

    /**
     * 构建部门树，该部门的父亲节点
     *
     * @param dept
     * @param allDepts
     * @param ownParentIds
     */
    private void buildOwnParent(Dept dept, List<Dept> allDepts, List<Long> ownParentIds) {
        allDepts
                .stream()
                .filter(deptTem -> dept.getParentId().equals(deptTem.getId()) && dept.getParentId().intValue() != 0)
                .forEach(deptTem -> {
                    buildOwnParent(deptTem, allDepts, ownParentIds);
                    ownParentIds.add(deptTem.getId());
                });
    }

    /**
     * 构建部门树，下拉列表使用
     *
     * @param dept
     * @param depts
     * @return
     */
    private List<TreeSelect> buildTreeselect(Dept dept, List<Dept> depts) {
        return depts.parallelStream()
                .filter(deptTmp -> deptTmp.getParentId().equals(dept.getId()))
                .map(deptTmp -> {
                    TreeSelect treeSelect = new TreeSelect();
                    treeSelect.setId(deptTmp.getId());
                    treeSelect.setLabel(deptTmp.getDeptName());
                    treeSelect.setChildren(buildTreeselect(deptTmp, depts));
                    return treeSelect;
                }).collect(Collectors.toList());
    }

    /**
     * 部门名称是否唯一
     *
     * @param dept
     */
    private void checkDeptNameUnique(Dept dept) {
        long cnt = baseMapper.selectCount(Wrappers.<Dept>lambdaQuery().eq(Dept::getDeptName, dept.getDeptName()).ne(dept.getId() != null, Dept::getId, dept.getId()));
        if (cnt > 0) {
            throw new ServiceException(SysRCode.DEPT_NAME_EXIST);
        }
    }

    /**
     * 孩子节点是否有启用状态
     *
     * @param dept
     */
    private void checkOwnChildEnabled(Dept dept) {
        if (EnabledEnum.NO.match(dept.getEnabled())) {
            List<Long> ownChildIds = new ArrayList<>();
            List<Dept> list = baseMapper.selectList(Wrappers.<Dept>lambdaQuery()
                    .notIn(Dept::getId, dept.getId()));
            buildOwnChild(dept, list, ownChildIds);
            if (CollectionUtils.isNotEmpty(ownChildIds)) {
                Long ownChildEnabledCnt = baseMapper.selectCount(Wrappers.<Dept>lambdaQuery().in(Dept::getId, ownChildIds).eq(Dept::getEnabled, EnabledEnum.YES.getValue()));
                if (ownChildEnabledCnt > 0) {
                    throw new ServiceException(SysRCode.DEPT_HAS_ENABLED_CHILD);
                }
            }
        }
    }

    /**
     * 父节点是否为禁用状态
     *
     * @param dept
     */
    private void checkOwnParentDisabled(Dept dept) {
        if (EnabledEnum.YES.match(dept.getEnabled()) || dept.getId() == null) {
            List<Long> ownParentIds = new ArrayList<>();
            List<Dept> list = baseMapper.selectList(Wrappers.<Dept>lambdaQuery()
                    .notIn(dept.getId() != null, Dept::getId, dept.getId()));
            buildOwnParent(dept, list, ownParentIds);
            if (CollectionUtils.isNotEmpty(ownParentIds)) {
                List<Dept> ownChildEnabled = baseMapper.selectList(Wrappers.<Dept>lambdaQuery().in(Dept::getId, ownParentIds).eq(Dept::getEnabled, EnabledEnum.NO.getValue()));
                if (CollectionUtils.isNotEmpty(ownChildEnabled)) {
                    String deptNames = StringUtils.join("【", ownChildEnabled.parallelStream().map(Dept::getDeptName).collect(Collectors.joining(",")), "】");
                    if (dept.getId() != null) {
                        throw new ServiceException(SysRCode.DEPT_HAS_DISABLED_PARENT_FOR_UPDATE, deptNames);
                    } else {
                        throw new ServiceException(SysRCode.DEPT_HAS_DISABLED_PARENT_FOR_UPDATE, deptNames);
                    }
                }
            }
        }
    }
}
